/**
 * Buffers vital sign updates and then sends them in batches to improve the 
 * message throughput.
 */
package bedside;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

import common.VitalSignListener;
import common.VitalSignUpdate;

/**
 * @author exl2878
 *
 */
public class VitalSignBuffer implements VitalSignListener, Serializable {

	private static final long DEFAULT_DELAY = 5000;
	
	/**
	 * Stores the vital sign updates until they are sent out to the 
	 * vital sign listeners.
	 */
	private final Collection<VitalSignUpdate> updateBuffer;
	
	/**
	 * List of objects that will receive batches of vital sign updates.  This 
	 * list may contain both local and remote objects.
	 */
	private final List<VitalSignListener> vsListeners;
	
	public VitalSignBuffer() {
		this( DEFAULT_DELAY );
	}
	
	public VitalSignBuffer( long maxDelay  ) {

		this.vsListeners = new LinkedList<VitalSignListener>();
		this.updateBuffer = Collections.synchronizedCollection( new LinkedList<VitalSignUpdate>() );
		
		// Schedule the buffer to transmit its contents at a fixed interval.
		Timer sendTimer = new Timer("Vital Sign Buffer Timer", true );
		TimerTask task = new TimerTask() {
			@Override
			public void run() {
				VitalSignBuffer.this.sendUpdates();
			}
		};
		sendTimer.scheduleAtFixedRate(task, maxDelay, maxDelay);
	}

	public void addVitalSignListener( VitalSignListener vsl ) {
		vsListeners.add( vsl );
	}
	
	public void removeVitalSignListener( VitalSignListener vsl ) {
		vsListeners.remove( vsl );
	}
	
	protected void sendUpdates() {
		// Since invoking the updateVitalSign method on remote objects may 
		// take a significant amount of time, we don't want to prevent the 
		// updateBuffer from receiving new vital sign readings.  We can 
		// avoid that issue by copying the updates in the updateBuffer to a 
		// separate list and then clearing the original updateBuffer.  This 
		// method will then use the buffer's copy so that the actual buffer 
		// is free to receive new vital sign updates.
		Collection<VitalSignUpdate> bufferCopy;
		synchronized( updateBuffer ) {
			// Create a copy of the original updateBuffer
			bufferCopy = new ArrayList<VitalSignUpdate>( updateBuffer );
			// Now clear out the original buffer
			updateBuffer.clear();
		}
		synchronized (vsListeners) {
			for (VitalSignListener vsl : vsListeners) {
				try {
					vsl.updateVitalSigns(bufferCopy);
				} catch (RemoteException re) {
					Logger.getLogger("err").log(Level.SEVERE, re.getLocalizedMessage());
				}
			}
		}
	}

	@Override
	public void updateVitalSign(VitalSignUpdate reading) throws RemoteException {
		updateBuffer.add( reading );
	}

	@Override
	public void updateVitalSigns(Collection<VitalSignUpdate> readings)
			throws RemoteException {
		
		updateBuffer.addAll( readings );
	}
}
